package com.sdp.right.flow.impl;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.sdp.core.bizc.exception.ApplicationException;
import com.sdp.core.bizc.exception.ExceptionMessage;
import com.sdp.core.bizc.threadLocal.ContextManager;
import com.sdp.core.bizc.utils.ApplicationContextUtils;
import com.sdp.core.bizc.utils.CoreConstantsEnum;
import com.sdp.core.bizc.vo.BusinessContext;
import com.sdp.right.db.sys_emp.service.ISysEmpService;
import com.sdp.right.db.sys_flow_current_task.service.ISysFlowCurrentTaskService;
import com.sdp.right.db.sys_flow_instance.service.ISysFlowInstanceService;
import com.sdp.right.db.sys_flow_node_attribute_version.service.ISysFlowNodeAttributeVersionService;
import com.sdp.right.db.sys_flow_node_jump_version.service.ISysFlowNodeJumpVersionService;
import com.sdp.right.db.sys_flow_task_his.service.ISysFlowTaskHisService;
import com.sdp.right.db.sys_flow_template.service.ISysFlowTemplateService;
import com.sdp.right.db.sys_flow_template_version.service.ISysFlowTemplateVersionService;
import com.sdp.right.entity.SysEmp;
import com.sdp.right.entity.SysFlowCurrentTask;
import com.sdp.right.entity.SysFlowInstance;
import com.sdp.right.entity.SysFlowNodeAttributeVersion;
import com.sdp.right.entity.SysFlowNodeJumpVersion;
import com.sdp.right.entity.SysFlowTaskHis;
import com.sdp.right.entity.SysFlowTemplate;
import com.sdp.right.entity.SysFlowTemplateVersion;
import com.sdp.right.flow.dto.FlowParam;
import com.sdp.right.flow.service.ISysFlowApproveService;
import com.sdp.right.flow.service.ISysFlowCallbackService;
import com.sdp.right.flow.service.ISysFlowService;
import com.sdp.right.utils.RightConstants;

import lombok.extern.slf4j.Slf4j;

@Service
@DS(value="right")
@Slf4j
public class SysFlowServiceImpl implements ISysFlowService{
	
	@Autowired
	private ISysFlowTemplateService iSysFlowTemplateService;
	
	@Autowired
	private ISysFlowTemplateVersionService iSysFlowTemplateVersionService;
	
	@Autowired
	private ISysFlowNodeAttributeVersionService iSysFlowNodeAttributeVersionService;
	
	@Autowired
	private ISysFlowNodeJumpVersionService iSysFlowNodeJumpVersionService;
	
	@Autowired
	private ISysFlowInstanceService iSysFlowInstanceService;
	
	@Autowired
	private ISysFlowTaskHisService iSysFlowTaskHisService;
	
	@Autowired
	private ISysFlowCurrentTaskService iSysFlowCurrentTaskService;
	
	@Autowired
	private RedissonClient redissonClient;
	
	@Autowired
	private ISysFlowApproveService iSysFlowApproveService;
	
	@Autowired
	private ISysEmpService iSysEmpService;

	/**
	 * 获取当前节点信息
	 * @param templateNo
	 * @param templateVersion
	 * @param nodeId
	 * @return
	 */
	@Override
	public FlowParam getCurrentNode(String templateNo, String templateVersion, String nodeId) {
		SysFlowNodeAttributeVersion attribute = iSysFlowNodeAttributeVersionService.queryFlowNodeAttributeVersion(templateVersion, templateNo, nodeId);
		List<SysFlowNodeJumpVersion> jumpList = iSysFlowNodeJumpVersionService.querySysFlowNodeJumpVersion(templateVersion, templateNo, nodeId);
		FlowParam param = new FlowParam();
		param.setCurrentNodeAttribute(attribute);
		param.setCurrentNodeJump(jumpList);
		return param;
	}

	/**
	 * 获取当前任务内码
	 * @param taskId
	 * @return
	 */
	@Override
	public FlowParam getCurrentTask(String taskId) {
		//判断是否登录
		SysEmp emp = iSysEmpService.querySysEmpByAccountNo(ContextManager.getContextData(CoreConstantsEnum.token中账号的属性名.getCode()));
		if(emp==null) {
			throw new ApplicationException("请先登录");
		}
		SysFlowCurrentTask task = iSysFlowCurrentTaskService.getById(taskId);
		if(task==null) {
			return null;
		}
		
		SysFlowTemplateVersion sysFlowTemplateVersion = iSysFlowTemplateVersionService.queryFlowTemplateVersion(task.getTemplateNo(), task.getTemplateVersion());
		FlowParam flowParam= getCurrentNode(task.getTemplateNo(), task.getTemplateVersion(), task.getCurrentNodeId());
		flowParam.setCurrentTask(task);
		flowParam.setTaskId(taskId);
		flowParam.setTemplateName(task.getTemplateName());
		flowParam.setTemplateNo(task.getTemplateNo());
		flowParam.setStartNode(sysFlowTemplateVersion.getStartNode());
		flowParam.setInstanceId(task.getInstanceId());
		return flowParam;
	}

	/**
	 * 获取已经审批过的任务记录
	 * @param instanceId
	 * @return
	 */
	@Override
	public List<SysFlowTaskHis> getApprovedFlowTaskHis(String instanceId) {
		return iSysFlowTaskHisService.queryapprovedFlowTaskHis(instanceId);
	}

	/**
	 * 初始化流程 主要是得到第一个环节信息
	 * @param templateNo
	 * @return
	 */
	@Override
	public FlowParam initFlow(String templateNo) {
		//判断是否登录
		SysEmp emp = iSysEmpService.querySysEmpByAccountNo(ContextManager.getContextData(CoreConstantsEnum.token中账号的属性名.getCode()));
		if(emp==null) {
			throw new ApplicationException("请先登录");
		}
		SysFlowTemplate sysFlowTemplate = iSysFlowTemplateService.queryFlowTemplate(templateNo);
		if(StringUtils.isEmpty(sysFlowTemplate) || StringUtils.isEmpty(sysFlowTemplate.getTemplateVersion())) {
			return null;
		}
		SysFlowTemplateVersion sysFlowTemplateVersion = iSysFlowTemplateVersionService.queryFlowTemplateVersion(templateNo, sysFlowTemplate.getTemplateVersion());
		if(sysFlowTemplateVersion==null) {
			return null;
		}
		FlowParam flowParam = ApplicationContextUtils.getBean(SysFlowServiceImpl.class).getCurrentNode(templateNo, sysFlowTemplateVersion.getTemplateVersion(), sysFlowTemplateVersion.getStartNode());
		flowParam.setTemplateName(sysFlowTemplateVersion.getTemplateName());
		flowParam.setTemplateNo(templateNo);
		flowParam.setStartNode(sysFlowTemplateVersion.getStartNode());
		return flowParam;
	}

	/**
	 * 流程启动
	 * @param flowParam
	 * @return
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public String startUpFlow(FlowParam flowParam) {
		if(flowParam==null) {
			throw new ApplicationException("提交的流程信息为空");
		}
		//判断是否登录
		SysEmp emp = iSysEmpService.querySysEmpByAccountNo(ContextManager.getContextData(CoreConstantsEnum.token中账号的属性名.getCode()));
		if(emp==null) {
			throw new ApplicationException("请先登录");
		}
		flowParam.setSysEmp(emp);
		//添加一条实例信息
		SysFlowInstance instance = iSysFlowInstanceService.createFlowInstance(flowParam);
		if(instance==null || StringUtils.isEmpty(instance.getKeyId())) {
			return null;
		}
		//设置流程实例内码
		flowParam.setInstanceId(instance.getKeyId());
		
		//任务历史表里增加一条记录，表示流程已经启动
		SysFlowTaskHis his = new SysFlowTaskHis();
		his.setCreateTime(LocalDateTime.now());
		his.setInstanceId(instance.getKeyId());
		his.setStartTime(LocalDateTime.now());
		his.setApprovalTime(LocalDateTime.now());
		his.setSystemNo(flowParam.getCurrentNodeAttribute().getSystemNo());
		his.setTemplateName(flowParam.getTemplateName());
		his.setSubmitEmpName(instance.getStartEmpName());
		his.setSubmitEmpNo(instance.getStartEmpNo());
		his.setApprovalEmpName(instance.getStartEmpName());
		his.setApprovalEmpNo(instance.getStartEmpNo());
		his.setCurrentNodeId(flowParam.getCurrentNodeAttribute().getNodeNo());
		his.setCurrentNodeLabel(flowParam.getCurrentNodeAttribute().getNodeLabel());
		his.setApprovalState(RightConstants.流程审批状态_通过.getCode());
		his.setTemplateNo(instance.getTemplateNo());
		his.setTemplateVersion(instance.getTemplateVersion());
		his.setProfilePicture(emp.getProfilePicture());

		
	
		BusinessContext businessContext = ApplicationContextUtils.getBean(SysFlowServiceImpl.class).commit(flowParam);
		if(businessContext.isSuccess()) {
			iSysFlowTaskHisService.save(his);
			return instance.getKeyId();
		}else {
			//删除添加的流程实例
			iSysFlowInstanceService.removeById(instance.getKeyId());
			return null;
		}
	}

	/**
	 * 流程提交
	 * @param flowParam
	 */
	@Override
	public BusinessContext comitFlow(FlowParam flowParam) {
		if(flowParam==null) {
			throw new ApplicationException("提交的流程信息为空");
		}
		//判断是否登录
		SysEmp emp = iSysEmpService.querySysEmpByAccountNo(ContextManager.getContextData(CoreConstantsEnum.token中账号的属性名.getCode()));
		if(emp==null) {
			throw new ApplicationException("请先登录");
		}
		flowParam.setSysEmp(emp);
		BusinessContext businessContext = new BusinessContext();
		RLock lock = redissonClient.getLock(flowParam.getInstanceId());
		try {
			if (lock.tryLock(5, TimeUnit.SECONDS)) {
				businessContext= ApplicationContextUtils.getBean(SysFlowServiceImpl.class).commit(flowParam);
			}else {
				businessContext.setMessage("系统繁忙，请稍后再试");
				businessContext.setSuccess(false);
			}
		} catch (InterruptedException e) {
			log.error(ExceptionMessage.getExceptionMessage(e));
			throw new RuntimeException(e);
		} finally {
			lock.unlock();
		}
		return businessContext;
	}
	
	
	/**
	 * 流程提交业务
	 * @param flowParam
	 */
	@Transactional(rollbackFor = Exception.class)
	public BusinessContext commit(FlowParam flowParam) {
		
		BusinessContext businessContext = new BusinessContext();
		//当前环节信息
		SysFlowNodeAttributeVersion nodeAttribute = flowParam.getCurrentNodeAttribute();
		
		ISysFlowCallbackService callback;
		
		//执行审批前回调
		if(!StringUtils.isEmpty(nodeAttribute.getNodeBeforeExecution())) {
			
			try {
				callback = (ISysFlowCallbackService) ApplicationContextUtils.getBean(Class.forName(nodeAttribute.getNodeBeforeExecution()));
				callback.execute(flowParam.getBusinessContext());
			} catch (Exception e) {
				e.printStackTrace();
				throw new ApplicationException(e.getMessage());
			}
			
		}
		
		//检验数据是否合法
		businessContext = iSysFlowApproveService.flowCheck(flowParam);
		if(!businessContext.isSuccess()) {
			throw new ApplicationException(businessContext.getMessage());
		}
		
		//当前环节审批
		businessContext = iSysFlowApproveService.flowApprove(flowParam);
		if(!businessContext.isSuccess()) {
			throw new ApplicationException(businessContext.getMessage());
		}
		
		//生成下个环节任务信息
		businessContext = iSysFlowApproveService.flowSubmit(flowParam);
		if(!businessContext.isSuccess()) {
			throw new ApplicationException(businessContext.getMessage());
		}

		//执行审批后回调
		if(!StringUtils.isEmpty(nodeAttribute.getNodeAfterExecution())) {
			try {
				callback = (ISysFlowCallbackService) ApplicationContextUtils.getBean(Class.forName(nodeAttribute.getNodeAfterExecution()));
				callback.execute(flowParam.getBusinessContext());
			} catch (Exception e) {
				e.printStackTrace();
				throw new ApplicationException(e.getMessage());
			}
			
		}
		
		//结束后的回调函数,这里主要是因为如果结束节点上配置了结束后回调，需要在这里执行
		//因为流程检测到下个节点是结束节点，就会结束流程，结束节点是个虚拟节点，不会执行。
		if(RightConstants.是.getCode().equals(flowParam.getIsEnd()) && flowParam.getEndNodeAttribute()!=null) {
			try {
				if(!StringUtils.isEmpty(flowParam.getEndNodeAttribute().getNodeAfterExecution())) {
					callback = (ISysFlowCallbackService) ApplicationContextUtils.getBean(Class.forName(flowParam.getEndNodeAttribute().getNodeAfterExecution()));
					callback.execute(flowParam.getBusinessContext());
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new ApplicationException(e.getMessage());
			}
		}
		return businessContext;
	}

	@Override
	public void removeFlowByInstanceId(String instanceId) {
		iSysFlowCurrentTaskService.removeFlowCurrentTaskByInstanceId(instanceId);
		iSysFlowTaskHisService.removeFlowTaskHisByInstanceId(instanceId);
		iSysFlowInstanceService.removeById(instanceId);
		
	}

}
